/* EasyWay Game Engine
 * Copyright (C) 2006 Daniele Paggi.
 *  
 * Written by: 2006 Daniele Paggi<dshnt@hotmail.com>
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.easyway.optimizer;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;

import org.easyway.annotations.Optimized;

public class InitOptimizer {

	/**
	 * indicates the classes on that implements the ILayerID
	 */
	public static final String layerIDclasses[] = {
			"org.easyway.objects.sprites2D.SimpleSprite",
			"org.easyway.tiles.TileManager",
			"org.easyway.objects.text.Text",
			"org.easyway.objects.text.EWText",
			"org.easyway.objects.text.RpgText",
			"org.easyway.objects.extra.ParticleEngine",
			"org.easyway.objects.extra.StarsManager",
			"org.easyway.objects.extra.StaticBackGround",
			// "org.easyway.objects.extra.StaticObject",
			"org.easyway.quad.QuadManager",
			"org.easyway.effects.translator.FadeOut",
			"org.easyway.effects.translator.FadeZoomOut",
			"org.easyway.effects.translator.FixedStripesOut",
			"org.easyway.effects.translator.HorizontalStripesOut",
			"org.easyway.effects.translator.ScrollOut" };

	/**
	 * indicates the packages that should be parsed by the Optimizer
	 */
	public static final String packages[] = { "game", "com", "org/easyway", "demoGame" };

	public static ArrayList<String> classes = new ArrayList<String>(300);

	/**
	 * indicates if the Optimizer should write on console the optimizing things
	 */
	public static boolean showOptimizierInfo = true;

	public static final void getClasses() {
		File dir;
		String child[];
		String abs;
		ArrayList<File> queue = new ArrayList<File>(100);
		String osName = System.getProperty("os.name");
		for (String s : packages) {
			abs = s;
			try {
				if (s.startsWith("/"))
					s = s.substring(1);
				while (s.indexOf("\\") != -1)
					s = s.substring(0, s.indexOf("\\")) + "/"
							+ s.substring(s.indexOf("\\") + 1);
				URL tmpUrl = Thread.currentThread().getContextClassLoader()
						.getResource(s);
				// ignore the not-found packages
				if (tmpUrl == null) {
					System.out
							.println(" InitOptimizer - ignored package(url): "
									+ s);
					continue;
				}
				s = tmpUrl.getFile();
				s = s.replace("%20", " ");
				if (osName.startsWith("Windows")) {
					if (s.charAt(0) == '/') // only on windows!
						s = s.substring(1);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

			dir = new File(s);
			child = dir.list();
			if (child == null) {
				System.out.println(" InitOptimizer - ignored package: " + s);
				continue;
			}

			File fscan;
			for (String scan : child) {
				if ((fscan = new File(s + '/' + scan)).isDirectory()) {
					queue.add(fscan);
				} else {
					if (scan.contains("$"))
						continue;
					if (!scan.contains(".class"))
						continue;

					scan = (abs + '.' + scan).replace('\\', '.');
					scan = scan.replace('/', '.');
					scan = scan.substring(0, scan.length() - 6);
					classes.add(scan);
					// System.out.println("_Analyzer_ class finded: " + scan);
					// System.out.println(scan);
				}
			}

			int start = s.indexOf(abs);
			while (queue.size() > 0) {
				dir = queue.get(0); // pop
				queue.remove(0); // pop

				child = dir.list();
				s = dir.getPath();

				if (child == null)
					continue;
				for (String scan : child) {
					if ((fscan = new File(s + '/' + scan)).isDirectory()) {
						queue.add(fscan);
					} else {
						if (scan.contains("$"))
							continue;
						if (!scan.contains(".class"))
							continue;
						scan = (s.substring(start) + '.' + scan).replace('\\',
								'.');
						scan = scan.replace('/', '.');
						scan = scan.substring(0, scan.length() - 6);
						classes.add(scan);
						// System.out.println("_Analyzer_ class finded: " +
						// scan);
					}
				}
			}
		}
		String array[] = new String[classes.size()];
		classes.toArray(array);
		// InitOptimizer.changeRenderForUEditor(array);
		System.out
				.println("_Analyzer_ number of classes finded by InitOptimizer: "
						+ classes.size());

	}

	/**
	 * loads all the classes in the classes ArrayList
	 */
	// public static final void getxClasses() {
	// File dir;
	// String child[];
	// String abs;
	// ArrayList<File> queue = new ArrayList<File>(100);
	// for (String s : packages) {
	// abs = s;
	// try {
	// if (s.startsWith("/"))
	// s = s.substring(1);
	// while (s.indexOf("\\") != -1)
	// s = s.substring(0, s.indexOf("\\")) + "/"
	// + s.substring(s.indexOf("\\") + 1);
	//
	// s = Thread.currentThread().getContextClassLoader().getResource(
	// s).getFile();
	// s = s.replace("%20", " ");
	// if (s.charAt(0) == '/')
	// s = s.substring(1);
	// } catch (Exception e) {
	// e.printStackTrace();
	// }
	//
	// dir = new File(s);
	// child = dir.list();
	// if (child == null) {
	// System.out.println("ERROR: " + s);
	// new Exception();
	// continue;
	// }
	//
	// File fscan;
	// for (String scan : child) {
	// if ((fscan = new File(s + '/' + scan)).isDirectory()) {
	// queue.add(fscan);
	// } else {
	// if (scan.contains("$"))
	// continue;
	// if (!scan.contains(".class"))
	// continue;
	//
	// scan = (abs + '.' + scan).replace('\\', '.');
	// scan = scan.substring(0, scan.length() - 6);
	// classes.add(scan);
	// // System.out.println(scan);
	// }
	// }
	//
	// int start = s.indexOf(abs);
	// while (queue.size() > 0) {
	// dir = queue.get(0); // pop
	// queue.remove(0); // pop
	//
	// child = dir.list();
	// s = dir.getPath();
	//
	// if (child == null)
	// continue;
	// for (String scan : child) {
	// if ((fscan = new File(s + '/' + scan)).isDirectory()) {
	// queue.add(fscan);
	// } else {
	// if (scan.contains("$"))
	// continue;
	// if (!scan.contains(".class"))
	// continue;
	// scan = (s.substring(start) + '.' + scan).replace('\\',
	// '.');
	// scan = scan.substring(0, scan.length() - 6);
	// classes.add(scan);
	// if (showOptimizierInfo)
	// System.out.println("_Optimizer_ class finded: "
	// + scan);
	// }
	// }
	// }
	// }
	// if (showOptimizierInfo)
	// System.out
	// .println("_Optimizer_ number of classes finded by InitOptimizer: "
	// + classes.size());
	// }
	public static final void optimizeLayerID() {
		String array[] = new String[classes.size()];
		classes.toArray(array);
		selfWriteLayerID(array);
	}

	/**
	 * enable the Debugging Core
	 * 
	 */
	public static void initDebugCore() {
		try {
			ClassPool cp = ClassPool.getDefault();
			CtClass ctClass = cp.get("org.easyway.system.Core");
			ctClass.stopPruning(true);
			ctClass.defrost();
			ctClass.getDeclaredMethod("coreLoop").setModifiers(Modifier.PUBLIC);
			ctClass.toClass();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Optimize all the classes that Implements the ILayerID interface
	 */
	public static final void selfWriteLayerID() {
		selfWriteLayerID(layerIDclasses);
	}

	/**
	 * Optimize the classes that implement the ILayerID interface
	 * 
	 * @param classes
	 *            the classes that implement the ILayerID interface and that
	 *            should be optimized
	 */
	public static void changeRenderForUEditor(String... classes) {
		ClassPool cp = ClassPool.getDefault();
		CtClass ctClasses[] = new CtClass[classes.length];
		// javassist.ClassClassPath x; //cp.getImportedPackages().n
		int index = 0;
		String s;
		// HotSwapper hs = null;
		// try {
		// hs = new HotSwapper(8000);
		// } catch (Exception e) {
		// e.printStackTrace();
		// }
		for (int j = 0; j < classes.length; ++j) {
			s = classes[j];
			try {
				CtClass ctClass = cp.get(s);

				// ctClass.stopPruning(true);
				// ctClass.defrost();
				if (ctClass.isInterface())
					continue;
				CtMethod ctMethod;
				ctMethod = ctClass.getDeclaredMethod("render");
				if (Modifier.isStatic(ctMethod.getModifiers()))
					continue;
				System.out.println("" + ctClass.getName());
				ctClasses[index] = ctClass;
				++index;

				ctMethod.insertAfter("{"
						+ "	org.easyway.system.StaticRef.onScreen.add($0);"
						+ "}");

			} catch (NotFoundException e) {
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		for (int i = 0; i < index; ++i) {
			try {
				// ctClasses[i].writeFile();
				// ctClasses[i].defrost();
				ctClasses[i].toClass();
				// hs.reload(ctClasses[i].getName(), ctClasses[i].toBytecode());
				// System.out.println("done");
				// ctClasses[i].toBytecode();
			} catch (Exception e) {
				// try {
				// hs
				// .reload(ctClasses[i].getName(), ctClasses[i]
				// .toBytecode());
				// } catch (Exception en) {
				// en.printStackTrace();
				// }
				e.printStackTrace();
			}
		}
	}

	public static void selfWriteLayerID(String... classes) {
		ClassPool cp = ClassPool.getDefault();
		CtClass ctClasses[] = new CtClass[classes.length];
		// javassist.ClassClassPath x; //cp.getImportedPackages().n
		int index = 0;
		String s;
		for (int j = 0; j < classes.length; ++j) {
			s = classes[j];
			try {
				CtClass ctClass = cp.get(s);

				// ctClass.stopPruning(true);
				// ctClass.defrost();
				{
					Object ctAnnotations[] = ctClass.getAnnotations();
					boolean finded = false;
					for (Object ann : ctAnnotations) {
						if (ann instanceof Optimized) {
							if (showOptimizierInfo)
								System.out.println("_Optimizer_  the class "
										+ s + " is already Optimized");
							finded = true;
							break;
						}
					}
					if (finded)
						continue;
				}

				CtClass ctInterfaces[] = ctClass.getInterfaces();
				for (int i = 0; i < ctInterfaces.length; ++i) {

					if (ctInterfaces[i].getName().equals(
							"org.easyway.interfaces.extended.ILayerID")) {
						if (showOptimizierInfo)
							System.out
									.println("_Optimizer_  Optimizing IDLayer of \""
											+ s + "\"");
						ctClasses[index] = ctClass;
						++index;
						CtField field = CtField.make(
								"protected int idLayer = -1;", ctClass);
						ctClass.addField(field);
						ctClass.getDeclaredMethod("getIdLayer").setBody(
								"{ return idLayer; }");
						ctClass
								.getDeclaredMethod("setIdLayer",
										new CtClass[] { CtClass.intType })
								.setBody(
										"{if (idLayer != -1) "
												+ "org.easyway.system.StaticRef.layers[idLayer].remove(this);"
												+ "if ($1 < 0) $1 = 0;"
												+ "else if ($1 > org.easyway.system.StaticRef.layers.length)"
												+ "$1 = org.easyway.system.StaticRef.layers.length-1;"
												+ "idLayer = $1;"
												+ "org.easyway.system.StaticRef.layers[idLayer].add(this); }");
						break;
					}
				}
				// ctClass.toClass();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		for (int i = 0; i < index; ++i) {
			try {
				// ctClasses[i].writeFile();

				ctClasses[i].toClass();
				// ctClasses[i].toBytecode();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}
